/* 
 * File:   Server.cpp
 * Author: brady
 * 
 * Created on October 17, 2010, 9:15 PM
 */

#include "Server.h"

#define buffer_size 64000

Server::Server(int server_port, string socket_type, int ipv) {
    if (socket_type == "tcp") {
        socketType = SOCK_STREAM;
    } else {
        socketType = SOCK_DGRAM;
    }

    if (ipv == 4) {
        IPv = AF_INET;
    } else {
        IPv = AF_INET6;
    }

    port = server_port;
    max_clients = 10;
    bound = false;

    BuildServer();
}

in_port_t Server::get_in_port(struct sockaddr *sa) {
    if (sa->sa_family == AF_INET) {
        return (((struct sockaddr_in*) sa)->sin_port);
    }

    return (((struct sockaddr_in6*) sa)->sin6_port);
}

void Server::SetSockType(string sock_type) {
    if (sock_type == "tcp") {
        socketType = SOCK_STREAM;
    } else {
        socketType = SOCK_DGRAM;
    }

    BuildServer();
}

void Server::SetIPv(int ipv) {
    if (ipv == 4) {
        IPv = AF_INET;
    } else {
        IPv = AF_INET6;
    }

    BuildServer();
}

void Server::SetPort(int server_port) {
    port = server_port;

    BuildServer();
}

//TCP ONLY FUNCTIONS

void Server::SetMaxClients(int n) {
    max_clients = n;
    BuildServer();
}

void Server::Listen() {
#ifdef DEBUG
    cout << "Listening...\n";
#endif
    listen(server_sockfd, max_clients);

}

int Server::Accept() {
    int c;
    c = GetFreeClient();
    socklen_t sz;
    sz = sizeof client_addresses[c];

    int cl;
    cl = accept(server_sockfd, &client_addresses[c], &sz);
    

#ifdef DEBUG
    if (cl == -1) {
        cout << "Accept failed\n";
    } else {
        cout << "Client Accepted\n" << "Client number is: " << c << "\n";
    }
#endif

    client_sockfds[c] = cl;
    return c;
}

void Server::SendString(int client, string msg) {
    void* data = (void*)msg.c_str();
    Send(client, data, msg.size());
}

void Server::Send(int client, void* data, int length) {
    send(client_sockfds[client], data, length, 0);
}

void Server::SendFile(int client, string prefix, string fpath, string suffix)
{
    string fullstring;
    ifstream file(fpath.c_str(), fstream::in);
    stringstream prefss;
    prefss << prefix;
    stringstream suffss;
    suffss << suffix;
    if (file.is_open()) {
        vector<char> data;
        //allocate room for prefix and read prefix into data
        data.resize(prefix.size());
        prefss.read(&data[0], prefix.size());
        while (!file.eof()) {
            //allocate an additional buffer_size bytes
            data.resize(data.size()+buffer_size);
            //read up to buffer_size bytes into the data vector from the file
            file.read(&data.at(data.size()-buffer_size), buffer_size);
            //trim all unused bytes from the end of the file.
            //if buffer_size bytes were read, trims 0 bytes.
            data.resize(data.size() - (buffer_size - file.gcount()));
        }
        //allocate some room for suffix and read it into data
        data.reserve(data.size() + suffix.size() + 1);
        suffss.read(&data[data.size() - suffix.size()], suffix.size());
        
        Send(client, data.data(), data.size());
    } else {
      //file didn't open, do something.
	cout << "Failed to open file\n";
    }
}

void Server::SendFile(int client, string fpath)
{
    SendFile(client, "", fpath, "");
}

int Server::Receive(int client, void* buffer, int bufferSize) {
    int recvcode;
    bzero(buffer, bufferSize);
    recvcode = recv(client_sockfds[client], buffer, bufferSize, 0);
    if (recvcode <= 0) {
        RemoveClient(client);
    }
    return recvcode;
}

bool Server::IsClientConnected(int client) {
    if (client_sockfds[client] == 0) {
        return false;
    } else return true;
}

int Server::ClientCount() {
    int count = 0;
    for (int client = 0; client < max_clients; client++) {
        if (IsClientConnected(client)) {
            count++;
        }
    }
    return count;
}

void Server::RemoveClient(int client) {
    close(client_sockfds[client]);
    client_sockfds[client] = 0;
}

//UDP ONLY FUNCTIONS

char* Server::ReceiveFromAny() {
    socklen_t size = (socklen_t) sizeof client_addresses[0];
    vector<char> recvBuffer;
    recvfrom(server_sockfd, &recvBuffer[0], buffer_size, 0, &client_addresses[0], &size);
    return &recvBuffer[0];
}

void Server::ReplyToLast(const char* msg) {
    socklen_t size = (socklen_t) sizeof client_addresses[0];
    sendto(server_sockfd, msg, strlen(msg), 0, &client_addresses[0], size);
}

int Server::GetFreeClient() {
    for (int c = 0; c < max_clients; c++) {
        if (!IsClientConnected(c)) {
            return c;
        }
    }
    return -1;
}



void Server::BuildServer() {

    int binderr;

    if (bound) {
        close(server_sockfd);
    }

    int yes = 1;

    client_sockfds = new int[max_clients];

    client_addresses = new struct sockaddr[max_clients];

    setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
            sizeof (int));


    if (IPv == AF_INET) {

        bzero(&server_addressV4, sizeof server_addressV4);

        server_addressV4.sin_family = IPv;
        server_addressV4.sin_addr.s_addr = INADDR_ANY;
        server_addressV4.sin_port = htons(port);
        server_sockfd = socket(IPv, socketType, 0);

        binderr = bind(server_sockfd, (struct sockaddr*) &server_addressV4, sizeof server_addressV4);
    } else {
        bzero(&server_addressV6, sizeof server_addressV6);

        server_addressV6.sin6_family = IPv;
        server_addressV6.sin6_addr = in6addr_any;
        server_addressV6.sin6_port = htons(port);
        server_sockfd = socket(IPv, socketType, 0);

        binderr = bind(server_sockfd, (struct sockaddr*) &server_addressV6, sizeof server_addressV6);
    }
    bound = true;

#ifdef DEBUG
    if (binderr == 0) {
        cout << "Bind Successful\n";
    } else {
        cout << "Bind Failed\n";
    }
#endif

}
